﻿Option Strict On

Imports System.Text
Imports System.Net
Imports System.Net.Sockets
Imports System.IO

Namespace Proxy
    ''' <summary>
    ''' Socks4a connection proxy class.  This class implements the Socks4a standard proxy protocol
    ''' which is an extension of the Socks4 protocol 
    ''' </summary>
    ''' <remarks>
    ''' In Socks version 4A if the client cannot resolve the destination host's domain name 
    ''' to find its IP address the server will attempt to resolve it.  
    ''' </remarks>
    Public Class Socks4aProxyClient
        Inherits Socks4ProxyClient
        Private Const PROXY_NAME As String = "SOCKS4a"

        ''' <summary>
        ''' Default constructor.
        ''' </summary>
        Public Sub New()
            MyBase.New()
        End Sub

        ''' <summary>
        ''' Creates a Socks4 proxy client object using the supplied TcpClient object connection.
        ''' </summary>
        ''' <param name="tcpClient">An open TcpClient object with an established connection.</param>
        Public Sub New(ByVal tcpClient As TcpClient)
            MyBase.New(tcpClient)
        End Sub

        ''' <summary>
        ''' Create a Socks4a proxy client object.  The default proxy port 1080 is used.
        ''' </summary>
        ''' <param name="proxyHost">Host name or IP address of the proxy server.</param>
        ''' <param name="proxyUserId">Proxy user identification information for an IDENTD server.</param>
        Public Sub New(ByVal proxyHost As String, ByVal proxyUserId As String)
            MyBase.New(proxyHost, proxyUserId)
        End Sub

        ''' <summary>
        ''' Create a Socks4a proxy client object.
        ''' </summary>
        ''' <param name="proxyHost">Host name or IP address of the proxy server.</param>
        ''' <param name="proxyPort">Port used to connect to proxy server.</param>
        ''' <param name="proxyUserId">Proxy user identification information.</param>
        Public Sub New(ByVal proxyHost As String, ByVal proxyPort As Integer, ByVal proxyUserId As String)
            MyBase.New(proxyHost, proxyPort, proxyUserId)
        End Sub

        ''' <summary>
        ''' Create a Socks4 proxy client object.  The default proxy port 1080 is used.
        ''' </summary>
        ''' <param name="proxyHost">Host name or IP address of the proxy server.</param>
        Public Sub New(ByVal proxyHost As String)
            MyBase.New(proxyHost)
        End Sub

        ''' <summary>
        ''' Create a Socks4a proxy client object.
        ''' </summary>
        ''' <param name="proxyHost">Host name or IP address of the proxy server.</param>
        ''' <param name="proxyPort">Port used to connect to proxy server.</param>
        Public Sub New(ByVal proxyHost As String, ByVal proxyPort As Integer)
            MyBase.New(proxyHost, proxyPort)
        End Sub

        ''' <summary>
        ''' Gets String representing the name of the proxy. 
        ''' </summary>
        ''' <remarks>This property will always return the value 'SOCKS4a'</remarks>
        Public Overrides ReadOnly Property ProxyName() As String
            Get
                Return PROXY_NAME
            End Get
        End Property


        ''' <summary>
        ''' Sends a command to the proxy server.
        ''' </summary>
        ''' <param name="proxy">Proxy server data stream.</param>
        ''' <param name="command">Proxy byte command to execute.</param>
        ''' <param name="destinationHost">Destination host name or IP address.</param>
        ''' <param name="destinationPort">Destination port number</param>
        ''' <param name="userId">IDENTD user ID value.</param>
        ''' <remarks>
        ''' This method override the SendCommand message in the Sock4ProxyClient object.  The override adds support for the
        ''' Socks4a extensions which allow the proxy client to optionally command the proxy server to resolve the 
        ''' destination host IP address. 
        ''' </remarks>
        Friend Overrides Sub SendCommand(ByVal proxy As NetworkStream, ByVal command As Byte, ByVal destinationHost As String, ByVal destinationPort As Integer, ByVal userId As String)
            ' PROXY SERVER REQUEST
            'Please read SOCKS4.protocol first for an description of the version 4
            'protocol. This extension is intended to allow the use of SOCKS on hosts
            'which are not capable of resolving all domain names.
            '
            'In version 4, the client sends the following packet to the SOCKS server
            'to request a CONNECT or a BIND operation:
            '
            '        +----+----+----+----+----+----+----+----+----+----+....+----+
            '        | VN | CD | DSTPORT |      DSTIP        | USERID       |NULL|
            '        +----+----+----+----+----+----+----+----+----+----+....+----+
            ' # of bytes:	   1    1      2              4           variable       1
            '
            'VN is the SOCKS protocol version number and should be 4. CD is the
            'SOCKS command code and should be 1 for CONNECT or 2 for BIND. NULL
            'is a byte of all zero bits.
            '
            'For version 4A, if the client cannot resolve the destination host's
            'domain name to find its IP address, it should set the first three bytes
            'of DSTIP to NULL and the last byte to a non-zero value. (This corresponds
            'to IP address 0.0.0.x, with x nonzero. As decreed by IANA  -- The
            'Internet Assigned Numbers Authority -- such an address is inadmissible
            'as a destination IP address and thus should never occur if the client
            'can resolve the domain name.) Following the NULL byte terminating
            'USERID, the client must sends the destination domain name and termiantes
            'it with another NULL byte. This is used for both CONNECT and BIND requests.
            '
            'A server using protocol 4A must check the DSTIP in the request packet.
            'If it represent address 0.0.0.x with nonzero x, the server must read
            'in the domain name that the client sends in the packet. The server
            'should resolve the domain name and make connection to the destination
            'host if it can. 
            '
            'SOCKSified sockd may pass domain names that it cannot resolve to
            'the next-hop SOCKS server.    

            '  userId needs to be a zero length string so that the GetBytes method
            '  works properly
            If userId Is Nothing Then
                userId = ""
            End If

            Dim destIp As Byte() = {0, 0, 0, 1}
            ' build the invalid ip address as specified in the 4a protocol
            Dim destPort As Byte() = GetDestinationPortBytes(destinationPort)
            Dim userIdBytes As Byte() = ASCIIEncoding.ASCII.GetBytes(userId)
            Dim hostBytes As Byte() = ASCIIEncoding.ASCII.GetBytes(destinationHost)
            Dim request As Byte() = New Byte(10 + userIdBytes.Length + (hostBytes.Length - 1)) {}

            '  set the bits on the request byte array
            request(0) = SOCKS4_VERSION_NUMBER
            request(1) = command
            destPort.CopyTo(request, 2)
            destIp.CopyTo(request, 4)
            userIdBytes.CopyTo(request, 8)
            ' copy the userid to the request byte array
            request(8 + userIdBytes.Length) = &H0
            ' null (byte with all zeros) terminator for userId
            hostBytes.CopyTo(request, 9 + userIdBytes.Length)
            ' copy the host name to the request byte array
            request(9 + userIdBytes.Length + hostBytes.Length) = &H0
            ' null (byte with all zeros) terminator for userId
            ' send the connect request
            proxy.Write(request, 0, request.Length)

            ' wait for the proxy server to send a response
            MyBase.WaitForData(proxy)

            ' PROXY SERVER RESPONSE
            ' The SOCKS server checks to see whether such a request should be granted
            ' based on any combination of source IP address, destination IP address,
            ' destination port number, the userid, and information it may obtain by
            ' consulting IDENT, cf. RFC 1413.  If the request is granted, the SOCKS
            ' server makes a connection to the specified port of the destination host.
            ' A reply packet is sent to the client when this connection is established,
            ' or when the request is rejected or the operation fails. 
            '
            '        +----+----+----+----+----+----+----+----+
            '        | VN | CD | DSTPORT |      DSTIP        |
            '        +----+----+----+----+----+----+----+----+
            ' # of bytes:	   1    1      2              4
            '
            ' VN is the version of the reply code and should be 0. CD is the result
            ' code with one of the following values:
            '
            '    90: request granted
            '    91: request rejected or failed
            '    92: request rejected becuase SOCKS server cannot connect to
            '        identd on the client
            '    93: request rejected because the client program and identd
            '        report different user-ids
            '
            ' The remaining fields are ignored.
            '
            ' The SOCKS server closes its connection immediately after notifying
            ' the client of a failed or rejected request. For a successful request,
            ' the SOCKS server gets ready to relay traffic on both directions. This
            ' enables the client to do I/O on its connection as if it were directly
            ' connected to the application server.

            ' create an 8 byte response array  
            Dim response As Byte() = New Byte(7) {}

            ' read the resonse from the network stream
            proxy.Read(response, 0, 8)

            '  evaluate the reply code for an error condition
            If response(1) <> SOCKS4_CMD_REPLY_REQUEST_GRANTED Then
                HandleProxyCommandError(response, destinationHost, destinationPort)
            End If
        End Sub



    End Class
End Namespace